package net.alche.sdd.servlet.biz;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cn.hutool.core.lang.ParameterizedTypeImpl;
import net.alche.al.util.AC;
import net.alche.al.util.Far;
import net.alche.al.util.M;
import net.alche.sdd.comm.anno.FaApi;
import net.alche.sdd.comm.anno.FaBean;
import net.alche.sdd.pojo.ApiConf;
import net.alche.sdd.pojo.ApiInfo;
import net.alche.sdd.pojo.ApiInfoParam;
import net.alche.sdd.pojo.KaAssist;

// 接口api解析
@SuppressWarnings("all")
public class SddApiParseBiz {
  List<ApiInfo> apiLs = new ArrayList<>();

  public void parseCtrl(FaBean ctrlAnn, Object ctrlBean, Method[] methods) {
    if (apiLs.isEmpty()) {
      Far.store.apiLs = apiLs;
    }
    String cname = ctrlBean.getClass().getSimpleName();
    for (int i = 0; i < apiLs.size(); i++) {
      ApiInfo item = apiLs.get(i);
      if (item.classMethod.equals(cname)) {
        apiLs.remove(i);
        i--;
      }
    }
    for (Method md : methods) {
      FaBean ann = md.getAnnotation(FaBean.class);
      if (ann != null) {
        String httpType = ann.type();
        if (httpType != null) {
          ApiInfo api = initCtrlPath(ctrlAnn, ann, ctrlBean, md);
          if (api != null) {
            api.classMethod = cname;
            apiLs.add(api);
          }
        }
      }
    }
    // AC.log("api.len=", apiLs.size());
  }

  private ApiInfo initCtrlPath(FaBean beanAnn, FaBean ann, Object bean, Method method) {
    String url = ann.value();
    String parPath = beanAnn.value();
    if (parPath.endsWith("/*")) {
      parPath = parPath.substring(0, parPath.length() - 2);
    }
    String type = ann.type();
    if (type.equals("delete")) {
      type = "del";
    }
    if (!parPath.endsWith("/") && !url.startsWith("/")) {
      url = "/" + url;
    }
    String path = parPath + url;
    if (url.equals("/*index*")) {
      path = "*index*";
      return null;
    }
    if (M.in(path, "/ht/{path}", "/api", "/statix/{path}") || path.startsWith("/ka/") || path.startsWith("/ide/")) {
      return null;
    }
    ApiInfo api = new ApiInfo();
    api.path = path;
    api.name = ann.log();
    api.type = type;
    api.back = new ApiInfoParam();
    api.rule = ann.rule();

    FaApi fapp = method.getAnnotation(FaApi.class);
    ApiConf conf = initApiConf(fapp);

    String[] fa_links = (String[]) M.readField(bean, "fa_links");
    String[] simpNames = new String[fa_links.length];
    for (int i = 0; i < fa_links.length; i++) {
      String[] sssb = fa_links[i].split("\\.");
      simpNames[i] = sssb[sssb.length - 1];
    }

    // return
    if (fapp != null && M.noEmp(fapp.reType())) {
      Class cls = getFa_linkClass(simpNames, fa_links, fapp.reType());
      api.back.type = fapp.reType();
      if (needDeep(cls)) {
        parsePojo(api.back, cls, conf.back, null);
      }
    } else {
      String returnName = method.getReturnType().getSimpleName();
      api.back.type = getFaType(returnName);
      if (needDeep(method.getReturnType())) {
        parsePojo(api.back, method.getReturnType(), conf.back, null);
      } else if (returnName.equals("List")) {
        parseListT(api.back, method.getGenericReturnType(), conf.back, null);
      }
    }
    // 入参
    if (fapp != null) {
      api.params = initP1ToP10(fapp);
      for (ApiInfoParam ap : api.params) {
        Class cls = getFa_linkClass(simpNames, fa_links, ap.type);
        if (needDeep(cls)) {
          parsePojo(ap, cls, conf, null);
          if (api.params.size() == 1) {
            api.params = ap.childs;
          }
        }
      }
      // 查询条件
      if (fapp.mode().equals("select") && M.noEmp(fapp.dao())) {
        Class cls = getFa_linkClass(simpNames, fa_links, fapp.dao());
        if (cls != null) {
          FaApi daoJsonApi = (FaApi) cls.getAnnotation(FaApi.class);
          if (daoJsonApi != null) {
            api.params = new ArrayList<>();
            String json = daoJsonApi.json().replaceAll("~2~", "\"").replaceAll("~1~", "\n");
            List<ApiInfoParam> apis = M.toJsonObj_list(json, ApiInfoParam.class);
            for (ApiInfoParam ap : apis) {
              api.params.add(ap);
            }
          }
        }
      }
    }
    return api;
  }

  List<ApiInfoParam> initP1ToP10(FaApi fapp) {
    List<ApiInfoParam> params = new ArrayList<>();
    initP1ToP10_add(params, fapp.p1());
    initP1ToP10_add(params, fapp.p2());
    initP1ToP10_add(params, fapp.p3());
    initP1ToP10_add(params, fapp.p4());
    initP1ToP10_add(params, fapp.p5());
    initP1ToP10_add(params, fapp.p6());
    initP1ToP10_add(params, fapp.p7());
    initP1ToP10_add(params, fapp.p8());
    initP1ToP10_add(params, fapp.p9());
    initP1ToP10_add(params, fapp.p10());
    return params;
  }

  void initP1ToP10_add(List<ApiInfoParam> params, String txt) {
    if (M.emp(txt))
      return;
    ApiInfoParam pa = new ApiInfoParam();
    String suf = M.subSuf(txt, ":");
    pa.name = M.subPre(txt, ":");
    pa.type = getFaType(M.subPre(suf, "|"));
    pa.remark = M.subSuf(suf, "|");
    params.add(pa);
  }

  Class getFa_linkClass(String[] simpNames, String[] fa_links, String name) {
    String fpath = null;
    for (int j = 0; j < simpNames.length; j++) {
      if (M.equal(name, simpNames[j])) {
        fpath = fa_links[j];
        break;
      }
    }
    if (fpath == null) {
      return null;
    }
    try {
      return Class.forName(fpath);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    return null;
  }

  ApiConf initApiConf(FaApi fapp) {
    ApiConf conf = new ApiConf();
    conf.back = new ApiConf();
    if (fapp == null) {
      return conf;
    }
    conf.mode = fapp.mode();
    if (M.noEmp(fapp.wipes())) {
      conf.wipes = fapp.wipes().split(",");
    }
    if (M.noEmp(fapp.cols())) {
      conf.cols = fapp.cols().split(",");
      strTuHump(conf.cols);
    }
    if (M.noEmp(fapp.reCols())) {
      conf.back.cols = fapp.reCols().split(",");
      strTuHump(conf.back.cols);
    }
    return conf;
  }

  void strTuHump(String[] cols) {
    for (int j = 0; j < cols.length; j++) {
      String item = cols[j];
      if (item.contains("_")) {
        cols[j] = M.underToHump(item);
      }
    }
  }

  void parseListT(ApiInfoParam pp, Object type, ApiConf conf, Map<String, Boolean> mp) {
    if (type instanceof ParameterizedType pt) {
      Type[] tts = pt.getActualTypeArguments();
      if (tts != null && tts.length > 0) {
        Class dcls = (Class) tts[0];
        pp.type = dcls.getSimpleName() + "[]";
        if (needDeep(dcls)) {
          parsePojo(pp, dcls, conf, mp);
        }
      }
    }
  }

  void parsePojo(ApiInfoParam pp, Class cls, ApiConf conf, Map<String, Boolean> mp) {
    if (mp == null) {
      mp = new HashMap<>();
    }
    if (mp.get(cls.getSimpleName()) != null)
      return;
    mp.put(cls.getSimpleName(), true);
    for (Field fd : cls.getDeclaredFields()) {
      if (Modifier.isStatic(fd.getModifiers())) {
        continue;
      }
      String colName = fd.getName();
      String tname = fd.getType().getSimpleName();
      if (pp.childs == null) {
        pp.childs = new ArrayList<>();
      }
      ApiInfoParam one = new ApiInfoParam();
      one.name = colName;
      one.type = getFaType(tname);
      FaApi faap = fd.getAnnotation(FaApi.class);
      if (faap != null) {
        if (M.noEmp(faap.ign())) {
          continue;
        }
        one.remark = faap.mk();
        if (M.noEmp(faap.len())) {
          one.len = M.toInt(faap.len());
        }
      }
      if (conf != null) {
        if ("save".equals(conf.mode) && (faap == null || faap.col() != 1)) {
          continue;
        } else if (M.contain(conf.wipes, colName)) {
          continue;
        } else if (conf.cols != null && !M.contain(conf.cols, colName)) {
          continue;
        }
      }
      pp.childs.add(one);
      if (needDeep(fd.getType())) {
        parsePojo(one, fd.getType(), null, mp);
      } else if (tname.equals("List")) {
        parseListT(one, fd.getGenericType(), null, mp);
      }
    }
  }

  boolean needDeep(Class cls) {
    if (cls == null) {
      return false;
    }
    return cls.getPackageName().startsWith("fx.");
  }

  String getFaType(String javaType) {
    switch (javaType) {
      case "Integer": {
        return "int";
      }
      case "String": {
        return "string";
      }
      case "Double": {
        return "double";
      }
      case "Map": {
        return "map";
      }
      case "Date": {
        return "date";
      }
      case "List": {
        return "any[]";
      }
      case "Object": {
        return "any";
      }
      case "ResultPack": {
        return "any";
      }
    }
    return javaType;
  }
}
